package cn.txun.csmall.passport.filter;


import cn.txun.csmall.commons.web.JsonResult;
import cn.txun.csmall.commons.web.ServiceCode;
import cn.txun.csmall.commons.security.LoginPrincipal;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;

/**
 * JWT过滤器：解决的问题包含接收JWT,解析JWT,将解析得到的数据创建为认证信息
 *          并存入SecurityContext
 */
@Slf4j
@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {

    @Value("${csmall.jwt.secret-key}")
    private String secretKey ;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        log.debug("JwtAuthorizationFilter开始执行....");
        //根据业内惯用做法，客户端会将JWT放在请求的消息头(Requst Header)中Authorization属性中
        String jwt = request.getHeader("Authorization");
        log.debug("客户端携带的jwt:{}",jwt);
       //判断客户端传来的JWT是否有效
       if(!StringUtils.hasText(jwt)){
            //放行，由Spring Security框架进行检查
            chain.doFilter(request,response);
            return;
        }
        // TODO 解析JWT过程中可能出现异常，需要处理
        //解析JWT
        Claims claims = null;
        response.setContentType("application/json;charset=utf-8");

        try {
             claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();
        }catch (MalformedJwtException e){//JWT格式有误，一定有人伪造了JWT
            String message = "非法访问！";
            log.warn(message);
            log.warn("解析JWT出现了MalformedJwtException，将向客户端响应错误信息",e);
            //把JsonResult对象转为json字符串
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_MALFORMED, message);
            String jsonString = JSON.toJSONString(jsonResult);
            //response.setContentType("application/json;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.println(jsonString);
            writer.close();
            return;
        }catch(SignatureException e){//JWT签名有误，一定有人伪造了JWT
            String message = "非法访问！";
            log.warn(message);
            log.warn("解析JWT出现了SignatureException，将向客户端响应错误信息",e);
            //把JsonResult对象转为json字符串
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_SIGNATURE, message);
            String jsonString = JSON.toJSONString(jsonResult);
            PrintWriter writer = response.getWriter();
            writer.println(jsonString);
            writer.close();
            return;
        }catch(ExpiredJwtException e){//JWT过期是正常的，需要好好提示
            String message = "您未登录时间过长，请重新登录！";
            log.warn(message);
            log.warn("解析JWT出现了ExpiredJwtException，将向客户端响应错误信息",e);
            //把JsonResult对象转为json字符串
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_EXPIRED, message);
            String jsonString = JSON.toJSONString(jsonResult);
            //response.setContentType("application/json;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.println(jsonString);
            writer.close();
            return;
        }catch(Throwable e){//捕获解析JWT过程中的未知异常
            String message = "服务器请稍后再试！";
            log.warn(message);
            log.warn("解析JWT出现了未知异常，请尽快处理，将该异常在过滤器中进行处理",e);
            //把JsonResult对象转为json字符串
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_UNAUTHORIZED, message);
            String jsonString = JSON.toJSONString(jsonResult);
            //response.setContentType("application/json;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.println(jsonString);
            writer.close();
            return;
        }
        Long id = claims.get("id",Long.class);
        String username = claims.get("username", String.class);
        //从jwt取出权限JSON字符串
        String authoritiesJSONString
                = claims.get("authoritiesJSONString",String.class);
        log.debug("id:{}",id);
        log.debug("username:{}",username);
        log.debug("authoritiesJSONString:{}",authoritiesJSONString);

        //创建认证信息
        Object principal = new LoginPrincipal().setId(id).setUsername(username);//可以是任何类型，暂时使用用户名
        Object credentials  = null;//本次不需要
        //将权限JsonString转化权限集合
        Collection<SimpleGrantedAuthority> authorities
                = JSON.parseArray(authoritiesJSONString,SimpleGrantedAuthority.class);

        Authentication authenticationResult
                = new UsernamePasswordAuthenticationToken(principal,credentials,authorities);
        //将认证结果存入SecurityContext(上下文)中
        SecurityContext securityContext = SecurityContextHolder.getContext();
        securityContext.setAuthentication(authenticationResult);
        //放行
        chain.doFilter(request,response);
    }
}
